/**
 * @(#) Restaurant.java
 */

package TheRestaurantOwner;

import java.util.Random;

public class Restaurant
{
	// The restaurant has a name, an address, and a city.
	private String name;
	private String address;
	private City city;

	// The initial budget of the restaurant is 10.000 euros.
	private int budget = 10000;
	
	// In the restaurant there are 3 waiters, 1 chef, and 1 barman.
	private Chef chef;
	private Barman barman;
	private Waiter[] waiters;

	// In the restaurant there are 9 tables.
	private Table[] tables;

	// The initial reputation score is 15.
	private int reputationPoints = 15;

	private Menu menu;
	private Player owner;

	private int ingredientsCost = 0;
	private int moneySpent = 0;
	
	public Restaurant( String name, String address )
	{
		this.name = name;
		this.address = address;
		
		city = new City(address);
		
		// Employees
		waiters = new Waiter[]{new Waiter(), new Waiter(), new Waiter()};
		chef = new Chef("TAX3");
		barman = new Barman();
		
		// Tables
		tables = new Table[9];
		for (int i = 0; i < tables.length; i++){
			tables[i] = new Table(i);
		}
		
		// Give employees random names (http://listofrandomnames.com/)
		String[] employeeNames = new String[]{"Nadene", "Lynne", "Julee", "Lewis", "Liana", "Willy", "Vicky", "Leandra", "Jason", "Julius"};
		String[] employeeSurnames = new String[]{"Crepeau", "Tee", "Vogelsang", "Searle", "Caraveo", "Vescio", "Vanasse", "Josey", "Dudney", "Frazer"};
		Random random = new Random();
		for (int i = 0; i < waiters.length; i++) {
			waiters[i].setName(employeeNames[random.nextInt(employeeNames.length)],
					employeeSurnames[random.nextInt(employeeSurnames.length)]);
		}
		chef.setName(employeeNames[random.nextInt(employeeNames.length)],
				employeeSurnames[random.nextInt(employeeSurnames.length)]);
		barman.setName(employeeNames[random.nextInt(employeeNames.length)],
				employeeSurnames[random.nextInt(employeeSurnames.length)]);
	}
	
	public void assignTablesToWaiters( )
	{
		for (Waiter waiter : waiters){
			waiter.clearAssignments();
		}
		
		int numTables = getNumberOfOccupiedTables();
		System.out.println("Assigning waiters to " + numTables + " tables");
		
		// The owner decides the number of tables to be assigned every day to each waiter.
		int nTable = 0;
		for (int nWaiter = 0; nWaiter < waiters.length; nWaiter++){
			Waiter waiter = waiters[nWaiter];
			int nWaiterTables;
			if (numTables == 9){
				nWaiterTables = 3; // All waiters must be fully occupied with 9 tables
			} else{
				// Each waiter can serve 3 tables at most
				Integer defValue = ((numTables - nTable) < 3) ? (numTables - nTable) : 3;
				if (defValue == 0){
					continue;
				}
				if (nWaiter == 2){
					nWaiterTables = defValue;
				} else {
					String nWaiterTablesStr = owner.input("How many tables (1-3) for waiter " + (nWaiter + 1) +
						" (" + waiter.getName() + " " + waiter.getSurname() + ")? ", defValue.toString());
					nWaiterTables = Integer.parseInt(nWaiterTablesStr);
				}
			}
			while (nWaiterTables != 0){
				if (nTable < numTables){
					// Each table is assigned to only 1 waiter.
					waiter.assignTable(tables[nTable]);
					nTable++;
				}
				nWaiterTables--;
			}
		}
	}
	
	public Reputation computeReputation( )
	{
		// The restaurant has a reputation.
		// The high reputation corresponds to at least 30 reputation points,
		// the medium reputation corresponds to at least 15 reputation points. Below 15 reputation points the
		// reputation of the restaurant is low.
		if (reputationPoints >= 30){
			return Reputation.high;
		} else if (reputationPoints >= 15){
			return Reputation.medium;
		}
		return Reputation.low;
	}
	
	public int getNumberOfOccupiedTables( )
	{
		// The reputation can be high. This ensures that all the tables are occupied.
		// If the reputation is medium, only 5 tables are occupied.
		// Finally the reputation can be low. In this case only 2 tables are occupied.
		switch (computeReputation()){
		case low:
			return 2;
		case medium:
			return 5;
		default:
			return 9;
		}
	}

	public void makePayment( int amount )
	{
		if (amount > budget){
			System.out.println("Ran out of money!");
		}
		budget -= amount;
		moneySpent += amount;
	}

	public void paySalaries( )
	{
		System.out.println("Paying salaries for the week");
		makePayment(chef.getSalary());
		makePayment(barman.getSalary());
		makePayment(waiters[0].getSalary());
		makePayment(waiters[1].getSalary());
		makePayment(waiters[2].getSalary());
	}

	// The suppliers of the restaurant are paid at the end of every week
	// for the ingredient needed to prepare food and beverages
	// (the assumption is that there is no waste of ingredients).
	public void paySuppliers( )
	{
		System.out.println("Paying for ingredients for the week: " + ingredientsCost);
		makePayment(ingredientsCost);
		ingredientsCost = 0;
	}
	
	// Consider that there are additional costs for
	// the entire month (rent, cleaning service, taxes, etc.) of 4.000 euros
	// to be deducted from the budget at the end of the month.
	public void payAdditionalCosts( )
	{
		System.out.println("Paying for additional costs: 4000");
		makePayment(4000);
	}

	public void processOrder( Order order, Client client, Waiter waiter )
	{
		ingredientsCost += order.getIngredientsCost();
		
		// Get money.
		Beverage[] beverages = order.getBeverages();
		Dish[] dishes = order.getDishes();
		budget += menu.getCost(beverages[0]);
		budget += menu.getCost(beverages[1]);
		budget += menu.getCost(dishes[0]);
		budget += menu.getCost(dishes[1]);
		
		// Every time a client is not satisfied for the service 1 reputation points is removed
		// (otherwise 1 point is added).
		if (client.isSatisfiedWithService(waiter)){
			reputationPoints++;
		} else{
			reputationPoints--;
		}
		
		// The same happens if a client is not satisfied by the food or if a client is not satisfied by the
		// beverages (again if the client is satisfied 1 point is added).
		if (client.isSatisfiedWithFood(beverages[0], barman, menu.getCost(beverages[0])) &&
				client.isSatisfiedWithFood(beverages[1], barman, menu.getCost(beverages[1])) &&
				client.isSatisfiedWithFood(dishes[0], chef, menu.getCost(dishes[0])) &&
				client.isSatisfiedWithFood(dishes[1], chef, menu.getCost(dishes[1]))){
			reputationPoints++;
		} else{
			reputationPoints--;
		}
	}
	
	public void receiveOrders( Client[] clients )
	{
		int nClient = 0;
		
		for (Waiter waiter : waiters){
			for (int i = 0; i < waiter.getAssignments().length; i++){
				// Make orders
				Client client1 = clients[nClient];
				Order order1 = client1.makeOrder(menu);
				nClient++;
				Client client2 = clients[nClient];
				Order order2 = client2.makeOrder(menu);
				nClient++;
				
				processOrder(order1, client1, waiter);
				processOrder(order2, client2, waiter);
			}
		}
	}

	public int getBudget( )
	{
		return budget;
	}

	public City getCity( )
	{
		return city;
	}

	public Employee[] getEmployees( ){
		Employee[] employees = new Employee[5];
		employees[0] = barman;
		employees[1] = chef;
		employees[2] = waiters[0];
		employees[3] = waiters[1];
		employees[4] = waiters[2];
		return employees;
	}
	
	public Menu getMenu( )
	{
		return menu;
	}

	public int getMoneySpent( )
	{
		return moneySpent;
	}

	public void setMenu( Menu menu )
	{
		this.menu = menu;
	}

	public String getName( )
	{
		return name;
	}

	public Player getOwner( )
	{
		return owner;
	}

	public void setOwner( Player owner )
	{
		this.owner = owner;
	}
}
